home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
Pakiet multimedia
/
Grafika i zdjecia
/
Edytory grafiki rastrowej i wektorowej
/
Inscape 0.44.1
/
Inkscape-0.44.1-1.win32.exe
/
share
/
extensions
/
SpSVG.pm
< prev
next >
Wrap
Text File
|
2006-09-06
|
9KB
|
352 lines
#!/usr/bin/perl -w
#
# SpSVG
#
# Perl module for sodipodi extensions
#
# This is a temporary hack that provides the following:
# * Some standard getopts (help, i/o, ids)
# * A way to exit that produces the error codes outlined in
# the extension specs (SpSVG::error)
# * A method that takes a function as its arguments and passes
# each specified element ('--id=foo --id=bar', 'ids=fooz,baaz',
# and so forth) as plain text to the function. The function is
# expected to return the processed version of this text.
#
# TODO:
#
# * Write POD
# * Exit with a friendly message if XML::XQL isn't installed
# * Decide how to implement the module interface
# * Move from XML::XQL to SVG/SVG::Parser (see below)
# * Make the process method more efficient (again, see below)
#
# Authors: Daniel Goude (goude@dtek.chalmers.se)
#
package SpSVG; # Think of a better name
use strict;
#use Carp;
use Exporter;
use Getopt::Long;
#use Data::Dumper; # For debugging
# From the SVG.pm documentation (actually
# http://roasp.com/tutorial/tutorial6.shtml):
#
# > Currently, version 2.0 of SVG.pm does not internally support DOM
# > traversiong functionality such as getting the children,siblings,or
# > parent of an element, so the interaction capability between SVG::Parser
# > and SVG is limited to manipulations of a known image. The next version
# > of SVG will support all these and more key functions which will make
# > SVG::Parser extremely useful.
#
# I plan to replace the /XML::XQL(::DOM)?/ code as soon as this is
# fixed.
#use SVG;
#use SVG::Parser;
use XML::XQL;
use XML::XQL::DOM;
use vars qw(@ISA @EXPORT $VERSION);
$VERSION = 1.02; # fixme: use SpSVG 1.01 doesn't raise exception.
@ISA = qw(Exporter);
# Symbols
@EXPORT = qw(
);
sub new {
my $self = {
status => make_status(),
name => '', # Name of script
usage => '', # Usage string
opt_help => [], # Used for --help
ids => [], # Array of ids that will be iterated over
# in process()
svg => '', # SVG document object
};
bless $self;
}
sub parse {
my $self = shift;
my $infile = $self->{'opts'}->{'file'};
my $xml;
{
local $/=undef;
if ($infile) {
open (IN, $infile) or
$self->error('IO_ERR', "Can't open $infile: $!\n");
$xml = <IN>;
close IN or
$self->error('IO_ERR', "Can't close $infile: $!\n");
} else {
$xml = <>;
}
}
$self->{'parser'} = new XML::DOM::Parser;
my $parser = $self->{'parser'};
my $svg = $parser->parse($xml) ||
$self->error('INPUT_ERR', "Couldn't parse input: $!.");
$self->{'svg'} = $svg;
}
# Return SVG document as a string
sub get {
my $self = shift;
my $string = $self->{'svg'}->toString;
}
# Print to $outfile|STDOUT
sub dump {
my $self = shift;
my $outfile = $self->{'opts'}->{'output'};
if ($outfile) {
open(OUT, ">$outfile") or
$self->error('IO_ERR', "Can't open $outfile for writing: $!\n");
print OUT $self->get;
close OUT or $self->error('IO_ERR', "Can't close $outfile: $!\n");
} else {
print $self->get;
}
}
sub process_ids {
my $self = shift;
my $func = shift;
my @ids = @{$self->{'ids'}};
# Apply a user supplied function to each id
foreach my $id (@ids) {
my $svg = $self->{'svg'};
#warn "ID: $id\n";
my @nodes = $svg->xql("//*[\@id = '$id']") or
$self->error('NOOP_ERR', "Couldn't find element $id.");
my $node = shift @nodes; # Ids are unique
# fixme: Add more checking.
# Call the user function on the node identified by $id
my $new_node = $func->($node->toString);
# Replace the comment with user generated SVG
my $parent = $node->getParentNode;
my $comment = $svg->createComment('SpSVG');
$parent->replaceChild($comment, $node);
my $output = $self->{'svg'}->toString;
$output =~ s/<!--SpSVG-->/$new_node/;
# Here the whole (new) document is parsed. Probably VERY inefficient,
# but at least you get syntax checking for free..
$self->{'svg'} = $self->{'parser'}->parse($output);
#print $self->{'svg'}->toString;
}
}
# Exit status codes
sub make_status {
my $self = shift;
my %status = (
0 => ["SUCCESS", "Extension exited gracefully"],
1 => ["GEN_FAIL", "General failure"],
2 => ["MEM_ERR", "Memory error"],
3 => ["IO_ERR", "File I/O error"],
4 => ["MATH_ERR", "Math error"],
5 => ["INPUT_ERR", "Input not understood (not valid SVG)"],
6 => ["NOOP_ERR", "Could not operate on any objects in this " .
"data stream"],
7 => ["ARG_ERR", "Incorrect script arguments"]
);
# Generate error subs dynamically
foreach my $exit_code (sort keys %status) {
eval "sub $status{$exit_code}[0] { $exit_code; }";
die $@ if $@;
}
return \%status;
}
# Create an option array suitable for Getopt::Long
sub make_opt_vals {
my $self = shift;
my @opt_desc = @_;
my @opt_vals;
my @opt_help = @{$self->{'opt_help'}};
foreach (@opt_desc) {
my %h = %$_;
foreach my $key (keys %h) {
#print "Key : $h{$key}\n";
if ($key eq 'opt') {
push @opt_vals, $h{'opt'};
} elsif ($key eq 'desc') {
my $option = $h{'opt'};
$option =~ s/([^=]+)=.+/$1/;
$option =~ s/([^|]+)/(length "$1" > 1 ? '--' : '-') . "$1"/eg;
push @opt_help, [$option, $h{'desc'}];
}
}
}
$self->{'opt_help'} = \@opt_help;
return @opt_vals;
}
# Parse command line options
sub get_opts {
my $self = shift;
my @user_opt_desc = @_;
my @opt_desc = (
{
opt => 'help|h',
desc => 'Display this help and exit.',
},
{
opt => 'version|v',
desc => 'Print version and exit.',
},
{
opt => 'file|F=s',
desc => 'Input file (default: STDIN).',
},
{
opt => 'output|o=s',
desc => 'Output file (default: STDOUT).',
},
{
opt => 'id=s@',
desc => 'svg id to operate on (can be multiple).',
},
{
opt => 'ids=s',
desc => 'Comma-separated list of svg ids to operate on.',
},
);
# Create option arrays for Getopt::Long
my @opt_vals = $self->make_opt_vals(@opt_desc);
my @user_opt_vals = $self->make_opt_vals(@user_opt_desc);
# Append user options
foreach (@user_opt_vals) {
push @opt_vals, $_;
}
# Where the parsed options are stored
my %opts;
#exit 0;
# Parse all options
GetOptions(\%opts, @opt_vals) or usage();
# Handle comma-separated 'ids=foo,bar'
my @ids = @{$opts{'id'}} if $opts{'id'};
if (exists $opts{'ids'} && $opts{'ids'} =~ /[\w\d_]+(,[\w\d_]+)*/) {
push (@ids, split(/,/, $opts{'ids'}));
}
# Display usage etc. (and exit)
exists $opts{'version'} && $self->version();
exists $opts{'help'} && $self->usage();
# Save id values for later processing
$self->{'ids'} = \@ids;
# Save options
$self->{'opts'} = \%opts;
# Return the options to script
return %opts;
}
# Exit with named exit status
sub error {
my $self = shift;
my $error_name = shift;
my $script_error_msg = shift || '';
my %status = %{$self->{'status'}};
foreach (keys %status) {
if ($status{$_}[0] eq $error_name) {
$! = $_; # Set exit status
# Commented out; let sodipodi handle the error code instead
#my $msg = ($status{$_}->[1] . ": $script_error_msg");
my $msg = "$script_error_msg";
die $msg;
}
}
# Will not be reached unless an improper error_name is given
$! = 255; # Exit status
warn "Illegal error code '$error_name' called from script\n";
}
# Some accessor methods
sub set_usage {
my $self = shift;
my $usage = shift || die "No usage string supplied!\n";
$self->{'usage'} = $usage;
}
sub set_name {
my $self = shift;
my $name = shift || die "No script name supplied!\n";
$self->{'name'} = $name;
}
# Print usage and exit
sub usage {
my $self = shift;
print "Usage: $self->{'name'} OPTIONS FILE\n";
print $self->{'usage'};
my @opt_help = @{$self->{'opt_help'}};
foreach (@opt_help) {
print pad($_->[0]) . $_->[1] . "\n";
}
exit ARG_ERR();
}
sub pad {
my $string = shift;
my $width = '20';
return $string . ' ' x ($width - length($string));
}
# Print version
sub version {
print "Uses SpSVG version $VERSION\n";
exit ARG_ERR();
}
# End of module; return something true
1;
__END__
DOCUMENTATION HERE